﻿#include "pch.h"
#include "Const.h"
#include "InputBox.h"
#include "LogonWindow.h"
#include "MainWindow.h"
#include "RegWindow.h"
#include "Resource.h"

BEGIN_MESSAGE_MAP(MainWindow, CDialog)
    ON_BN_CLICKED(IDC_BUTTON_LOGON, OnButtonLogonClicked)
    ON_BN_CLICKED(IDC_BUTTON_REG, OnButtonRegClicked)
    ON_BN_CLICKED(IDC_BUTTON_ADD_FRIEND, OnButtonAddFriendClicked)
    ON_BN_CLICKED(IDC_BUTTON_MCHAT, OnButtonMChatClicked)
    ON_BN_CLICKED(IDC_BUTTON_LOGOFF, OnButtonLogoffClicked)
    ON_MESSAGE(Const::UM_CHAT_C2S, OnChatC2S)
    ON_MESSAGE(Const::UM_CHAT_S2C, OnChatS2C)
    ON_MESSAGE(Const::UM_FRIEND_LOGOFF, OnFriendLogoff)
    ON_MESSAGE(Const::UM_FRIEND_LOGON, OnFriendLogon)
    ON_MESSAGE(Const::UM_GET_ADD_FRIEND_REQUEST, OnGetAddFriendRequest)
    ON_MESSAGE(Const::UM_GET_ADD_FRIEND_RESULT, OnGetAddFriendResult)
    ON_MESSAGE(Const::UM_GET_LOGON_RESULT, OnGetLogonResult)
    ON_MESSAGE(Const::UM_GET_REG_RESULT, OnGetRegResult)
    ON_NOTIFY(NM_DBLCLK, IDC_LIST_VIEW_USERS, OnListFriendsDoubleClicked)
END_MESSAGE_MAP()

/**
 * 构造方法。
 * @param parent    父窗口
 */
MainWindow::MainWindow(CWnd* parent) :
    CDialog(IDD_MAIN_WINDOW, parent),
    mClient(NULL) {
}

/**
 * 析构方法。
 */
MainWindow::~MainWindow() {
    // 清除所有聊天窗口
    this->ClearChatWindow();

    // 删除客户端
    if (mClient != NULL) {
        delete mClient;
    }
}

/**
 * 添加所有好友到好友列表控件中。
 * @param result    登录结果
 */
void MainWindow::AddAllFriends(LogonResult* result) {
    for (int i = 0; i < result->mFriendCount; i++) {
        this->AddOneFriend(result->mFriends[i].mUsername, result->mFriends[i].mIconIndex, result->mFriendsOnline[i]);
    }

    CString text;
    text.Format(IDS_FRIEND_COUNT, result->mFriendCount);
    mStatusBar.SetText(text, 1, 0);
}

/**
 * 添加一个好友到好友列表控件中。
 * @param friendName    好友名
 * @param iconIndex     好友头像索引
 * @param isOnline      好友在线则为 true，否则为 false
 */
void MainWindow::AddOneFriend(LPCTSTR friendName, int iconIndex, BOOL isOnline) {
    if (iconIndex < 0) {
        return;
    }
    mListViewUsers.AddItem(friendName, iconIndex, isOnline);
}

/**
 * 改变界面状态。
 * @param isLogon   已登录则为 true，否则为 false
 */
void MainWindow::ChangeState(BOOL isLogon) {
    CToolBarCtrl& toolBarCtrl = mToolBar.GetToolBarCtrl();
    toolBarCtrl.EnableButton(IDC_BUTTON_LOGON, !isLogon);
    toolBarCtrl.EnableButton(IDC_BUTTON_REG, !isLogon);
    toolBarCtrl.EnableButton(IDC_BUTTON_ADD_FRIEND, isLogon);
    toolBarCtrl.EnableButton(IDC_BUTTON_MCHAT, isLogon);
    toolBarCtrl.EnableButton(IDC_BUTTON_LOGOFF, isLogon);

    CString text;
    if (isLogon) {
        text.LoadString(IDS_STATUS_LOGON);
        mStatusBar.SetText(text, 0, 0);
    } else {
        text.LoadString(IDS_STATUS_LOGOFF);
        mStatusBar.SetText(text, 0, 0);
    }
    text.Format(IDS_FRIEND_COUNT, 0);
    mStatusBar.SetText(text, 1, 0);
}

/**
 * 清除所有聊天窗口。
 */
void MainWindow::ClearChatWindow() {
    CString key;
    ChatWindow* value = NULL;
    POSITION pos = mChats.GetStartPosition();
    while (pos) {
        mChats.GetNextAssoc(pos, key, value);
        delete value;
    }
    mChats.RemoveAll();
}

/**
 * 获取指定好友名对应的聊天窗口。
 * @param friendName    好友名
 * @return 好友名对应的聊天窗口
 */
ChatWindow* MainWindow::GetChatWindow(LPCTSTR friendName) {
    // 查找用户名对应的聊天窗口
    ChatWindow* window = NULL;
    BOOL found = mChats.Lookup(friendName, window);

    if (!found) {
        // 未找到，则新建一个
        window = new ChatWindow(this, friendName);
        mChats.SetAt(friendName, window);
    }

    return window;
}

/**
 * 获取结果信息。
 * @param resultCode    结果码
 * @param text          返回的结果信息
 */
void MainWindow::GetResultMessage(ResultCode resultCode, CString* text) {
    switch (resultCode) {
        case ADD_FRIEND_ALLOW:
            text->LoadString(IDS_ADD_FRIEND_ALLOW);
            break;
        case ADD_FRIEND_NOT_ALLOW:
            text->LoadString(IDS_ADD_FRIEND_NOT_ALLOW);
            break;
        case ALREADY_BE_FRIENDS:
            text->LoadString(IDS_ALREADY_BE_FRIENDS);
            break;
        case FRIEND_NAME_EQUAL_SELF_NAME:
            text->LoadString(IDS_FRIEND_NAME_EQUAL_SELF_NAME);
            break;
        case NETWORK_ERROR:
            text->LoadString(IDS_NETWORK_ERROR);
            break;
        case PASSWORD_WRONG:
            text->LoadString(IDS_PASSWORD_WRONG);
            break;
        case REG_SUCCEEDED:
            text->LoadString(IDS_REG_SUCCEEDED);
            break;
        case TOO_MANY_FRIENDS:
            text->LoadString(IDS_TOO_MANY_FRIENDS);
            break;
        case USER_ALREADY_LOGON:
            text->LoadString(IDS_USER_ALREADY_LOGON);
            break;
        case USER_EXISTS:
            text->LoadString(IDS_USER_EXISTS);
            break;
        case USER_NOT_FOUND:
            text->LoadString(IDS_USER_NOT_FOUND);
            break;
    }
}

/**
 * "添加好友"按钮的单击响应方法。
 */
void MainWindow::OnButtonAddFriendClicked() {
    // 显示输入窗口，让用户输入好友名
    CString tipText;
    tipText.LoadString(IDS_PLEASE_INPUT_USERNAME);
    InputBox inputBox(this, tipText);
    if (inputBox.DoModal() == IDCANCEL) {
        return;
    }

    // 执行添加好友
    CString username(inputBox.GetStringValue());
    ResultCode resultCode = mClient->AddFriend(username);
    if (resultCode != SUCCEEDED) {
        this->GetResultMessage(resultCode, &tipText);
        AfxMessageBox(tipText, MB_ICONWARNING);
    }
}

/**
 * "离线"按钮的单击响应方法。
 */
void MainWindow::OnButtonLogoffClicked() {
    // 设置各种控件的状态
    this->ChangeState(FALSE);

    // 清空各种列表
    mListViewUsers.DeleteAllItems();
    this->ClearChatWindow();

    // 发送离线请求
    mClient->Logoff();
}

/**
 * "登录"按钮的单击响应方法。
 */
void MainWindow::OnButtonLogonClicked() {
    // 显示登录窗口
    LogonWindow logonWindow(this);
    INT_PTR clickedButton = logonWindow.DoModal();
    if (clickedButton == IDOK) {
        // 登录
        mClient->Logon(logonWindow.GetUsername(), logonWindow.GetPassword());
    }
}

/**
 * "群发消息"按钮的单击响应方法。
 */
void MainWindow::OnButtonMChatClicked() {
    this->GetChatWindow(ImProtocol::MULTI_CHAT)->ShowWindow(SW_SHOW);
}

/**
 * "注册"按钮的单击响应方法。
 */
void MainWindow::OnButtonRegClicked() {
    // 显示注册窗口
    RegWindow regWindow(this);
    INT_PTR clickedButton = regWindow.DoModal();
    if (clickedButton == IDOK) {
        // 注册
        mClient->Reg(regWindow.GetUsername(), regWindow.GetPassword(), regWindow.GetIconIndex());
    }
}

/**
 * 聊天事件的响应方法（客户端到服务器）。
 * @param wParam    聊天信息
 * @return 总是零
 */
LRESULT MainWindow::OnChatC2S(WPARAM wParam, LPARAM) {
    mClient->Chat((ChatInfo*)wParam);
    return 0;
}

/**
 * 聊天事件的响应方法（服务器到客户端）。
 * @param wParam    聊天信息
 * @return 总是零
 */
LRESULT MainWindow::OnChatS2C(WPARAM wParam, LPARAM) {
    ChatInfo* chatInfo = (ChatInfo*)wParam;
    CString dstUsername(chatInfo->mDstUsername);
    ChatWindow* chatWindow;
    if (dstUsername == ImProtocol::MULTI_CHAT) {
        chatWindow = this->GetChatWindow(ImProtocol::MULTI_CHAT);
    } else {
        chatWindow = this->GetChatWindow(chatInfo->mSrcUsername);
    }
    chatWindow->SetContent(chatInfo);
    chatWindow->ShowWindow(SW_SHOW);
    return 0;
}

/**
 * 好友离线事件的响应方法。
 * @param wParam    离线请求
 * @return 总是零
 */
LRESULT MainWindow::OnFriendLogoff(WPARAM wParam, LPARAM) {
    LogoffRequest* request = (LogoffRequest*)wParam;
    mListViewUsers.ChangeItemColor(request->mUsername, FALSE);
    return 0;
}

/**
 * 好友登录事件的响应方法。
 * @param wParam    好友登录信息
 * @return 总是零
 */
LRESULT MainWindow::OnFriendLogon(WPARAM wParam, LPARAM) {
    FriendLogon* friendLogon = (FriendLogon*)wParam;
    mListViewUsers.ChangeItemColor(friendLogon->mUsername, TRUE);
    return 0;
}

/**
 * 得到添加好友请求事件的响应方法。
 * @param wParam    添加好友请求
 * @return 总是零
 */
LRESULT MainWindow::OnGetAddFriendRequest(WPARAM wParam, LPARAM) {
    AddFriendRequest* request = (AddFriendRequest*)wParam;
    CString srcUsername(request->mSrcUsername);
    CString message(srcUsername);
    message.LoadString(IDS_CONFIRM_ADD_FRIEND);
    message = srcUsername + message;
    INT_PTR clickedButton = AfxMessageBox(message, MB_ICONQUESTION | MB_YESNO);
    BOOL allow = clickedButton == IDYES;
    mClient->AllowAddFriend(allow, request);
    if (allow) {
        this->AddOneFriend(request->mSrcUsername, request->mIconIndex, TRUE);
        CString text;
        text.Format(IDS_FRIEND_COUNT, mClient->GetFriendCount());
        mStatusBar.SetText(text, 1, 0);
    }

    return 0;
}

/**
 * 得到添加好友结果事件的响应方法。
 * @param wParam    添加好友结果
 * @return 总是零
 */
LRESULT MainWindow::OnGetAddFriendResult(WPARAM wParam, LPARAM) {
    AddFriendResult* result = (AddFriendResult*)wParam;
    CString text;
    this->GetResultMessage(result->mResultCode, &text);
    CString message(text);
    if (result->mResultCode == ADD_FRIEND_ALLOW) {
        this->AddOneFriend(result->mDstUsername, result->mDstIconIndex, TRUE);
        message.Format(text, result->mDstUsername);
        text.Format(IDS_FRIEND_COUNT, mClient->GetFriendCount());
        mStatusBar.SetText(text, 1, 0);
        AfxMessageBox(message, MB_ICONINFORMATION);
    } else if (result->mResultCode == ALREADY_BE_FRIENDS || 
        result->mResultCode == ADD_FRIEND_NOT_ALLOW ||
        result->mResultCode == USER_NOT_FOUND) {
        message = result->mDstUsername + message;
        AfxMessageBox(message, MB_ICONWARNING);
    }

    return 0;
}

/**
 * 得到登录结果事件的响应方法。
 * @param wParam    登录结果
 * @return 总是零
 */
LRESULT MainWindow::OnGetLogonResult(WPARAM wParam, LPARAM) {
    LogonResult* result = (LogonResult*)wParam;
    if (result->mResultCode == LOGON_SUCCEEDED) {
        this->ChangeState(TRUE);
        this->AddAllFriends(result);
    } else {
        CString message;
        this->GetResultMessage(result->mResultCode, &message);
        AfxMessageBox(message, MB_ICONERROR);
    }

    return 0;
}

/**
 * 得到注册结果事件的响应方法。
 * @param wParam    注册结果
 * @return 总是零
 */
LRESULT MainWindow::OnGetRegResult(WPARAM wParam, LPARAM) {
    RegResult* result = (RegResult*)wParam;
    CString message;
    this->GetResultMessage(result->mResultCode, &message);
    if (result->mResultCode == REG_SUCCEEDED) {
        this->ChangeState(TRUE);
        AfxMessageBox(message, MB_ICONINFORMATION);
    } else {
        AfxMessageBox(message, MB_ICONERROR);
    }

    return 0;
}

/**
 * 初始化事件的响应方法。
 * @return 总是 TRUE
 */
BOOL MainWindow::OnInitDialog() {
    CDialog::OnInitDialog();

    // 创建工具栏
    DWORD style = WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS;
    mToolBar.CreateEx(this, TBSTYLE_FLAT | TBSTYLE_TOOLTIPS, style);
    CToolBarCtrl& toolBarCtrl = mToolBar.GetToolBarCtrl();

    // 设置工具栏图片尺寸和按钮尺寸
    HINSTANCE instance = AfxGetInstanceHandle();
    HICON icon = LoadIcon(instance, MAKEINTRESOURCE(IDI_LOGON));
    ICONINFO iconInfo = { 0 };
    GetIconInfo(icon, &iconInfo);
    int iconWidth = iconInfo.xHotspot * 2;
    int iconHeight = iconInfo.yHotspot * 2;
    toolBarCtrl.SetBitmapSize(CSize(iconWidth, iconHeight));
    toolBarCtrl.SetButtonSize(CSize(iconWidth, iconHeight + iconHeight / 4));

    // 设置工具栏图片和按钮
    TBBUTTON tbButtons[] = {
        { 0, IDC_BUTTON_LOGON, TBSTATE_ENABLED, 0, 0, 0, 0 },
        { 1, IDC_BUTTON_REG, TBSTATE_ENABLED, 0, 0, 0, 0 },
        { 2, IDC_BUTTON_ADD_FRIEND, 0, 0, 0, 0, 0 },
        { 3, IDC_BUTTON_MCHAT, 0, 0, 0, 0, 0 },
        { 4, IDC_BUTTON_LOGOFF, 0, 0, 0, 0, 0 }
    };
    int buttonCount = sizeof(tbButtons) / sizeof(TBBUTTON);
    CBitmap bitmap;
    bitmap.CreateCompatibleBitmap(CDialog::GetDC(), iconWidth * buttonCount, iconHeight);
    toolBarCtrl.AddBitmap(buttonCount, &bitmap);
    toolBarCtrl.AddButtons(buttonCount, tbButtons);

    // 设置工具栏按钮的图标
    CImageList* imageList = toolBarCtrl.GetImageList();
    CToolTipCtrl* toolTipCtrl = toolBarCtrl.GetToolTips();
    for (int i = 0; i < buttonCount; i++) {
        // 替换工具栏按钮的图标
        icon = LoadIcon(instance, MAKEINTRESOURCE(IDI_LOGON + i));
        imageList->Replace(i, icon);
    }
    toolBarCtrl.SetImageList(imageList);

    // 设置工具栏按钮的提示文字
    toolTipCtrl->UpdateTipText(IDS_LOGON, &mToolBar, IDC_BUTTON_LOGON);
    toolTipCtrl->UpdateTipText(IDS_REG, &mToolBar, IDC_BUTTON_REG);
    toolTipCtrl->UpdateTipText(IDS_ADD_FRIEND, &mToolBar, IDC_BUTTON_ADD_FRIEND);
    toolTipCtrl->UpdateTipText(IDS_MCHAT, &mToolBar, IDC_BUTTON_MCHAT);
    toolTipCtrl->UpdateTipText(IDS_LOGOFF, &mToolBar, IDC_BUTTON_LOGOFF);

    // 设置工具栏位置
    CDialog::RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);

    // 设置用户列表控件
    mListViewUsers.SubclassDlgItem(IDC_LIST_VIEW_USERS, this);
    mListViewUsers.SetExtendedStyle(LVS_EX_FULLROWSELECT);
    CRect rect;
    CDialog::GetClientRect(rect);
    int width = rect.Width() - GetSystemMetrics(SM_CXVSCROLL) - 3;
    mListViewUsers.InsertColumn(0, _T(""), 0, width);

    // 设置状态栏
    mStatusBar.Create(WS_CHILD | WS_VISIBLE | CCS_BOTTOM, rect, this, AFX_IDW_STATUS_BAR);
    CDialog::GetWindowRect(rect);
    int parts[Const::STATUS_BAR_PART_COUNT] = {
        rect.Width() / 2,
        rect.Width()
    };
    mStatusBar.SetParts(Const::STATUS_BAR_PART_COUNT, parts);
    CString text;
    text.LoadString(IDS_STATUS_LOGOFF);
    mStatusBar.SetText(text, 0, 0);
    text.Format(IDS_FRIEND_COUNT, 0);
    mStatusBar.SetText(text, 1, 0);

    // 创建客户端
    try {
        mClient = new Client(this);
    } catch (ResultCode resultCode) {
        this->GetResultMessage(resultCode, &text);
        AfxMessageBox(text, MB_ICONERROR);
        exit(0);
    }

    return TRUE;
}

/**
 * 双击好友列表控件的响应方法。
 * @param nmhdr     被双击的表项
 * @param result    结果
 */
void MainWindow::OnListFriendsDoubleClicked(NMHDR* nmhdr, LRESULT* result) {
    LPNMITEMACTIVATE nmItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(nmhdr);
    CString friendName(mListViewUsers.GetItemText(nmItemActivate->iItem, 0));
    if (friendName.IsEmpty()) {
        return;
    }
    this->GetChatWindow(friendName)->ShowWindow(SW_SHOW);
    *result = 0;
}
